home *** CD-ROM | disk | FTP | other *** search
- /*------------------------------------------------------------------------------
- Copyright (c) 2008 Ensolis, LLC. All Rights Reserved.
- ----------------------------------------------------------------------------*/
-
- /******************************************************************************
- * File I/O Constants
- *****************************************************************************/
- const MODE_RDONLY = 0x01;
- const MODE_WRONLY = 0x02;
- const MODE_CREATE = 0x08;
- const MODE_APPEND = 0x10;
- const MODE_TRUNCATE = 0x20;
-
- const XML_MIMETYPE = "text/xml";
- const XML_ENCODING = "UTF-8";
- const TEXT_MIMETYPE = "text/plain";
- const TEXT_ENCODING = "UTF-8";
- /******************************************************************************
- * Create a error message to log.
- *
- * @param Original message.
- * @param Prefix to the message.
- * @return Message used for logging.
- *****************************************************************************/
- function createMessage(aMessage, aPrefix)
- {
- return (new Date()).toUTCString() + ": " + aPrefix + ": " + aMessage + "\r\n";
- }
-
- /******************************************************************************
- * Reads a file from disk and creates a dom document or text string.
- * This implements the nsIRunnable interface so it could
- * be dispatched to a thread.
- *
- * @param File to read.
- * @param The mimetype to read.
- * @return The content property can be read once the reader has run.
- *****************************************************************************/
- function FileReader(aFile, aMimetype)
- {
- this.file = aFile;
- this.mimetype = aMimetype;
- }
- FileReader.prototype = {
- _content: null,
- _file: null,
- _mimetype: null,
-
- get content() { return this._content; },
- set content(aVal) { this._content = aVal; },
-
- get file() { return this._file; },
- set file(aVal) { this._file = aVal; },
-
- get mimetype() { return this._mimetype; },
- set mimetype(aVal) { this._mimetype = aVal; },
-
- ///////////////////////////
- // nsISupports
- QueryInterface: function FileReader_QueryInterface(aIID)
- {
- if (!aIID.equals(Ci.nsIRunnable) &&
- !aIID.equals(Ci.nsISupports))
- throw Cr.NS_ERROR_NO_INTERFACE;
- return this;
- },
-
- ///////////////////////////
- // nsIRunnable
- run: function FileReader_run()
- {
- //make sure the file exists
- if (!this.file.exists()) {
- this.content = null;
- this.file = null;
- this.mimetype = null;
- return;
- }
-
- //create file input stream
- var fiStream = Cc["@mozilla.org/network/file-input-stream;1"].
- createInstance(Ci.nsIFileInputStream);
-
- //initialize stream
- fiStream.init(this.file, MODE_RDONLY, PERMS_FILE, false);
-
- //read XML mimetype
- if (this.mimetype == XML_MIMETYPE)
- this._readXML(fiStream);
-
- //read text mimetype
- else
- this._readText(fiStream);
-
- //close stream
- fiStream.close();
-
- //remove variable references
- this.file = null;
- this.mimetype = null;
- },
-
- /**
- * Read a XML file.
- *
- * @param The file input stream.
- */
- _readXML: function FileReader__readXML(aStream)
- {
- /**
- * to support earlier versions we need to use a buffered
- * input stream. See bug #287409
- */
- var biStream = Cc["@mozilla.org/network/buffered-input-stream;1"].
- createInstance(Ci.nsIBufferedInputStream);
- biStream.init(aStream, 64 * 1024);
-
- //parse dom document from stream
- var domParser = Cc["@mozilla.org/xmlextras/domparser;1"].
- createInstance(Ci.nsIDOMParser);
- this.content = domParser.parseFromStream(biStream, XML_ENCODING,
- biStream.available(),
- XML_MIMETYPE);
-
- //close the buffer input stream
- biStream.close();
- },
-
- /**
- * Read a text file.
- *
- * @param The file input stream.
- */
- _readText: function FileReader__readText(aStream)
- {
- //get a scriptable stream
- var siStream = Cc["@mozilla.org/scriptableinputstream;1"].
- createInstance(Ci.nsIScriptableInputStream);
- siStream.init(aStream);
-
- //convert to text encoding
- var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
- createInstance(Ci.nsIScriptableUnicodeConverter);
- converter.charset = TEXT_ENCODING;
-
- //read stream into a string
- var content = new String();
- while (siStream.available() > 0) {
- var chunk = siStream.read(siStream.available());
- content += converter.ConvertToUnicode(chunk);
- }
- this.content = content;
-
- //close scritable stream
- siStream.close();
- }
- };
-
- /******************************************************************************
- * Writes content to disk. The content can be a string, or a dom document.
- * This implements the nsIRunnable interface so it could be dispatched.
- * to a thread.
- *
- * @param File to write.
- * @param Content to write. Either a dom document, or a string.
- * @param If a backup copy of the file should be created.
- * @param The mimetype of the content to write. Used to determine write
- * mode and if content is dom or string.
- *****************************************************************************/
- function FileWriter(aFile, aContent, aBackup, aMimeType)
- {
- //set variables
- this.content = aContent;
- this.file = aFile;
- this.backup = aBackup;
- this.mimetype = aMimeType;
- }
- FileWriter.prototype = {
- _content: null,
- _file: null,
- _backup: null,
- _mimetype: null,
-
- get content() { return this._content; },
- set content(aVal) { this._content = aVal; },
-
- get file() { return this._file; },
- set file(aVal) { this._file = aVal; },
-
- get backup() { return this._backup; },
- set backup(aVal) { this._backup = aVal; },
-
- get mimetype() { return this._mimetype; },
- set mimetype(aVal) { this._mimetype = aVal; },
-
- ///////////////////////////
- // nsISupports
- QueryInterface: function FileWriter_QueryInterface(aIID)
- {
- if (!aIID.equals(Ci.nsIRunnable) &&
- !aIID.equals(Ci.nsISupports))
- throw Cr.NS_ERROR_NO_INTERFACE;
- return this;
- },
-
- ///////////////////////////
- // nsIRunnable
- run: function FileWriter_run()
- {
- //make sure the file exists
- if (!this.file.exists())
- this.file.create(this.file.NORMAL_FILE_TYPE, PERMS_FILE);
-
- //create a file output stream
- var foStream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
- createInstance(Ci.nsIFileOutputStream);
-
- //write XML mimetype
- if (this.mimetype == XML_MIMETYPE)
- this._writeXML(foStream);
-
- //write text mimetype
- else
- this._writeText(foStream);
-
- //close stream
- if (foStream instanceof Ci.nsISafeOutputStream)
- foStream.finish();
- foStream.close();
-
- //create a backup
- if (this.backup)
- this._createBackup();
-
- //remove variable references
- this.content = null;
- this.file = null;
- this.backup = null;
- this.mimetype = null;
- },
-
- /**
- * Write a xml file.
- *
- * @param The file output stream.
- */
- _writeXML: function FileWriter__writeXML(aStream)
- {
- //initialize stream
- aStream.init(this.file, (MODE_WRONLY | MODE_TRUNCATE), PERMS_FILE, 0);
-
- //serialize to stream
- var domSerializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"].
- createInstance(Ci.nsIDOMSerializer);
- domSerializer.serializeToStream(this.content, aStream, XML_ENCODING);
- },
-
- /**
- * Write a text file.
- *
- * @param The file output stream.
- */
- _writeText: function FileWriter__writeText(aStream)
- {
- //initialize stream
- aStream.init(this.file, (MODE_WRONLY | MODE_APPEND), PERMS_FILE, 0);
-
- //get the string converter
- var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
- createInstance(Ci.nsIScriptableUnicodeConverter);
- converter.charset = TEXT_ENCODING;
-
- //write to stream
- var chunk = converter.ConvertFromUnicode(this.content);
- aStream.write(chunk, chunk.length);
- var fin = converter.Finish();
- if (fin.length > 0)
- aStream.write(fin, fin.length);
- },
-
- /**
- * Create a backup copy of the file being written to.
- */
- _createBackup: function FileWriter__createBackup()
- {
- //replace file extension with .bak
- var name = this.file.leafName;
- var ext = name.substring(name.lastIndexOf(".") + 1, name.length);
- name = name.replace(ext, "bak");
-
- //get the backup file
- var backup = this.file.parent.clone();
- backup.append(name);
-
- //remove it if it already exists
- try {
- if (backup.exists())
- removeFile(backup);
-
- //copy the new file to the backup location
- this.file.copyTo(backup.parent, backup.leafName);
- } catch(e) {}
- }
- };
-
- /******************************************************************************
- * Interfaces used by a service for disk I/O functions. Supplies a set
- * of often used disk utilities.
- *
- * @status FROZEN
- * @version 1.0
- ******************************************************************************/
- function DiskService()
- {
- //setup additional interfaces
- this._ifaces.push(Ci.nsIObserver);
- this._ifaces.push(Ci.nsISupportsWeakReference);
-
- //setup a new error
- this._error = Cc["@ensolis.com/forecastfox/error-item;1"].
- createInstance(Ci.ffIErrorItem);
- }
- DiskService.prototype = {
- __proto__: new ServiceBase("DiskService"),
- _ioSvc: null,
- _domParser: null,
- _fileWriters: null,
- _writeTimer: null,
- _types: null,
-
- ///////////////////////////
- // nsIObserver
- observe: function DiskService_observe(aSubject, aTopic, aData)
- {
- if (aTopic != "timer-callback" || aSubject != this._writeTimer)
- return;
-
- //run the next stored writers
- for (var path in this._fileWriters) {
- this._fileWriters[path].run();
- delete this._fileWriters[path];
- break;
- }
-
- //control the starting-stopping of the timer
- this._controlTimer();
- },
-
- ////////////////////////////////
- // ffIService
-
- /**
- * Initialize the component. Called by the manager service.
- */
- start: function DiskService_start()
- {
- //setup writers object
- this._fileWriters = {};
-
- //setup types object
- this._types = {};
-
- //get io service
- this._ioSvc = Cc["@mozilla.org/network/io-service;1"].
- getService(Ci.nsIIOService);
-
- //get the dom parser
- this._domParser = Cc["@mozilla.org/xmlextras/domparser;1"].
- createInstance(Ci.nsIDOMParser);
-
- //return success
- return true;
- },
-
- /**
- * Destroy the component. Called by the manager service. This may be
- * called prior to start so it needs to be safe.
- */
- stop: function DiskService_stop()
- {
- //stop the writer timer
- if (this._writeTimer)
- this._writeTimer.cancel();
-
- //clear any pending writes
- if (this._fileWriters) {
- for (var path in this._fileWriters) {
- this._fileWriters[path].run();
- delete this._fileWriters[path];
- }
- }
-
- //clear variables
- this._ioSvc = null;
- this._domParser = null;
- this._fileWriters = null;
- this._writeTimer = null;
- this._types = null;
- },
-
- ////////////////////////////////
- // ffIDiskService
-
- /**
- * Get a file from one of our directory types. Creates a unique
- * file name if the type is the temp directory.
- *
- * @param Name of the file to get.
- * @param Directory type to get the file from.
- * @return File requested.
- */
- get: function DiskService_get(aName, aType)
- {
- var file = null;
-
- //use the cached type file
- if (this._types.hasOwnProperty(aType))
- file = this._types[aType];
-
- //get directory based on type
- else {
- switch (aType) {
- case TYPE_PROFILE:
- file = getKeyedDirectory("ProfD", ["forecastfox"], true);
- break;
- case TYPE_CACHE:
- file = getKeyedDirectory("ProfD", ["forecastfox", "cache"], true);
- break;
- case TYPE_ICONS:
- file = getKeyedDirectory("ProfD", ["forecastfox", "icons"], true);
- break;
- case TYPE_TEMP:
- file = getKeyedDirectory("TmpD", [], false);
- break;
- case TYPE_DEFAULTS:
- file = getInstallDirectory(["defaults"]);
- break;
- case TYPE_WEATHERFOX:
- file = getKeyedDirectory("ProfD", ["weatherfox"], false);
- break;
- case TYPE_ERRORS:
- file = getKeyedDirectory("ProfD", ["forecastfox", "errors"], true);
- break;
- }
-
- //cache the type
- this._types[aType] = file;
- }
-
- //clone the directory and append the file name
- file = file.clone();
- file.append(aName);
-
- //create unique if temp directory
- if (aType == TYPE_TEMP)
- file.createUnique(file.NORMAL_FILE_TYPE, PERMS_FILE);
-
- //return the file
- return file;
- },
-
- /**
- * Copy a file. It first removes the destination file
- * and then copies to the destination file.
- *
- * @param File to be copied.
- * @param File that will be replaced. It may or may not exist.
- */
- copy: function DiskService_copy(aFrom, aTo)
- {
- //remove the to file if it exists
- if (aTo.exists())
- removeFile(aTo);
-
- //copy from file
- aFrom.copyTo(aTo.parent, aTo.leafName);
- },
-
- /**
- * Clear a directory of the type passed in. This should be used
- * very carefully since will wipe out the directory.
- *
- * @param Type of directory to clear
- * @param Only remove files with cache in the name.
- */
- clear: function DiskService_clear(aType, aCache)
- {
- //get the folder of the requested type
- var folder = this.get("", aType);
- if (!folder.exists())
- return;
-
- //make sure its a directory
- if (!folder.isDirectory())
- return;
-
- //get list of files
- var files = folder.directoryEntries;
- while (files.hasMoreElements()) {
- var file = files.getNext().QueryInterface(Ci.nsIFile);
-
- //remove the file regardless
- if (!aCache)
- removeFile(file);
-
- //only remove cache files
- else {
- var name = file.leafName;
- if (name.indexOf("cache") != -1)
- removeFile(file);
- }
- }
- },
-
- /**
- * Reads a file from disk and converts it to a dom document.
- *
- * @param File to read.
- * @return The dom document.
- */
- read: function DiskService_read(aFile)
- {
- return this._dispatchRead(aFile, XML_MIMETYPE);
- },
-
- /**
- * Reads a file from disk and returns the string content.
- *
- * @param File to read.
- * @return The string content.
- */
- readText: function DiskService_readText(aFile)
- {
- return this._dispatchRead(aFile, TEXT_MIMETYPE);
- },
-
- /**
- * Writes a dom document to a file.
- *
- * @param File to write to.
- * @param Document to write.
- * @param Create a backup of the file.
- * @param Run in blocking mode.
- */
- write: function DiskService_write(aFile, aDoc, aBackup, aBlocking)
- {
- this._dispatchWrite(aFile, aDoc, aBackup, aBlocking, XML_MIMETYPE);
- },
-
- /**
- * Writes a strings content to a file.
- *
- * @param File to write to.
- * @param String to write.
- * @param Create a backup of the file.
- * @param Run in blocking mode.
- * @param Append to file instead of replace.
- */
- writeText: function DiskService_writeText(aFile, aContent, aBackup,
- aBlocking, aAppend)
- {
- //get content of the file if we are appending
- var content = "";
- if (aAppend)
- content = this.readText(aFile);
- content += aContent;
-
- //dispatch writer
- this._dispatchWrite(aFile, content, aBackup, aBlocking, TEXT_MIMETYPE);
- },
-
- /**
- * Creates an Empty dom document. Need to pass the local name of the
- * root node, doctype url, and namespace url.
- *
- * @param Local name of the root node.
- * @param Doctype url.
- * @param Namespace url.
- * @return An empty DOM document.
- */
- create: function DiskService_create(aRoot, aDTD, aNS)
- {
- //string representation of xml
- var contents = "";
- contents += "<?xml version=\"1.0\"?>\n" +
- "<!DOCTYPE " + aRoot + " SYSTEM \"" + aDTD + "\">\n" +
- "<" + aRoot + " version=\"0.9.10\" xmlns=\"" +
- aNS + "\"/>";
-
- //return dom document
- return this._domParser.parseFromString(contents, XML_MIMETYPE);
- },
-
- /*
- * Test for a valid dom document.
- *
- * @param Dom document to validate.
- * @param Local name of the root node.
- * @return True if document passed and root node is correct.
- */
- validate: function DiskService_validate(aDoc, aRoot)
- {
- //object passed
- if(!aDoc)
- return false;
-
- //incorrect root
- if (aDoc.documentElement.localName != aRoot)
- return false;
-
- return true;
- },
-
- /**
- * Gets the url of a file passed in. If the file doesn't exist,
- * a blank string is returned.
- *
- * @param File to get the url of.
- * @return Spec of the URI.
- */
- getFileURL: function DiskService_getFileURL(aFile)
- {
- //use ioservice to create uri
- var URI = this._ioSvc.newFileURI(aFile);
-
- //return the spec
- return URI.spec;
- },
-
- /**
- * Write a message to the error log. Any of the params can be null.
- * They are skipped if null.
- *
- * @param Message to write.
- * @param Exception that occurred.
- * @param File to copy.
- */
- log: function DiskService_log(aMessage, aError, aFile)
- {
- //get the error log file
- var file = this.get("errors.log", TYPE_ERRORS);
-
- //create a variable to hold the strings
- var content = "";
-
- //append the new message
- if (aMessage)
- content += createMessage(aMessage, "Message" );
-
- //append the error
- if (aError) {
- content += createMessage(aError.toString(), "Exception");
-
- //write call stack
- var frame = aError.location;
- while (frame) {
- content += createMessage(frame.toString(), "Stack");
- frame = frame.caller;
- }
- }
-
- //apend the file
- if (aFile) {
-
- //get the error directory
- var copyFile = this.get("", TYPE_ERRORS);
-
- //determine file name
- var name = aFile.leafName;
- if (name.indexOf(".") == -1)
- name = name + "-err";
- else {
- var ind = name.indexOf(".");
- var ext = name.substring(ind, name.length);
- name = name.substring(0, ind);
- name = name + "-err" + ext;
- }
-
- //copy file to new name and directory
- copyFile.append(name);
- copyFile.createUnique(copyFile.NORMAL_FILE_TYPE, PERMS_FILE);
- this.copy(aFile, copyFile);
-
- //add message
- content += createMessage("Error file (" + copyFile.leafName +
- ") created for " + aFile.leafName + ".", "File");
- }
-
- //write the content to disk
- if (content.length > 0)
- this.writeText(file, content, false, false, true);
- },
-
- ////////////////////////////////
- // Internal Functions
-
- /**
- * Dispatch the file reader. This helper function is used
- * because the different functions in the interface are used for different
- * mimetypes.
- *
- * @param File to read from.
- * @param Mimetype being read.
- */
- _dispatchRead: function DiskService__dispatchRead(aFile, aMimeType)
- {
- //perform any pending writes before reading
- var path = aFile.path;
- if (this._fileWriters.hasOwnProperty(path))
- return this._fileWriters[path].content;
-
- //create a file reader and dispatch it.
- var reader = new FileReader(aFile, aMimeType);
- reader.run();
-
- //get the created document and return it
- return reader.content;
- },
-
- /**
- * Dispatch the file writer. This helper function is used
- * because the different functions in the interface are used for different
- * mimetypes.
- *
- * @param File to write to.
- * @param string to write or document to write.
- * @param Create a backup of the file.
- * @param Run in blocking mode.
- * @param Mimetype being written.
- */
- _dispatchWrite: function DiskService__dispatchWrite(aFile, aContent, aBackup,
- aBlocking, aMimeType)
- {
- //if we have a pending write to the file remove it
- if (this._fileWriters.hasOwnProperty(aFile.path))
- delete this._fileWriters[aFile.path];
-
- //create the file writer
- var writer = new FileWriter(aFile, aContent, aBackup, aMimeType);
-
- //run inline if blocking
- if (aBlocking)
- writer.run();
-
- //add to pending writer list
- else
- this._fileWriters[aFile.path] = writer;
-
- // control the starting or stopping of the write timer
- this._controlTimer();
- },
-
- _controlTimer: function DiskService__controlTimer() {
-
- // determine if we have pending writes
- var pending = false;
- for (var path in this._fileWriters) {
- pending = true;
- break;
- }
-
- // we have pending writes and the timer isn't started
- if (pending && !this._writeTimer) {
- this._writeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
- this._writeTimer.init(this, 60*1000, Ci.nsITimer.TYPE_REPEATING_SLACK);
-
- // no pending writes and the timer is started
- } else if (!pending && this._writeTimer) {
- this._writeTimer.cancel();
- this._writeTimer = null;
- }
- }
- };